﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using YArchitech.Revit;
using YJKGeometryFuncs;

namespace YJKArchBaseCode.OutWallSearch
{
	public class OutWallFilter
	{
		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId viewId, ref List<WallData> outWallList, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			List<bool> list = new List<bool>();
			return OutWallFilter.Search(doc, viewId, ref outWallList, ref list, includLinkDocument, IsOpenTran);
		}

		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId viewId, ref List<WallData> outWallList, ref List<bool> isOrientationRight, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			return OutWallFilter.SearchByPlanView(doc, viewId, ref outWallList, ref isOrientationRight, includLinkDocument, IsOpenTran);
		}

		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId levelId, List<Wall> wallsByLevel, ref List<WallData> outWallList, bool includLinkDocument = false)
		{
			List<bool> list = new List<bool>();
			return OutWallFilter.SearchByLevel(doc, levelId, wallsByLevel, ref outWallList, ref list, includLinkDocument);
		}

		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId viewId, ref List<Wall> outWallList, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			List<bool> list = new List<bool>();
			return OutWallFilter.Search(doc, viewId, ref outWallList, ref list, includLinkDocument, IsOpenTran);
		}

		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId viewId, ref List<Wall> outWallList, ref List<bool> isOrientationRight, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			List<WallData> list = new List<WallData>();
			bool result = OutWallFilter.SearchByPlanView(doc, viewId, ref list, ref isOrientationRight, includLinkDocument, IsOpenTran);
			foreach (WallData wallData in list)
			{
				Wall item = wallData.Doc.GetElement(wallData.Id) as Wall;
				outWallList.Add(item);
			}
			return result;
		}

		public static bool Search(Autodesk.Revit.DB.Document doc, ElementId levelId, List<Wall> wallsByLevel, ref List<Wall> outWallList, bool includLinkDocument = false)
		{
			List<WallData> list = new List<WallData>();
			List<bool> list2 = new List<bool>();
			bool result = OutWallFilter.SearchByLevel(doc, levelId, wallsByLevel, ref list, ref list2, includLinkDocument);
			foreach (WallData wallData in list)
			{
				Wall item = wallData.Doc.GetElement(wallData.Id) as Wall;
				outWallList.Add(item);
			}
			return result;
		}

		private static bool SearchByLevel(Autodesk.Revit.DB.Document doc, ElementId levelId, List<Wall> wallsByLevel, ref List<WallData> outWallList, ref List<bool> isOrientationRight, bool includLinkDocument)
		{
			List<RoomData> roomDataList = new List<RoomData>();
			List<WallData> collection = new List<WallData>();
			if (!OutWallFilter.GetRoomDataInfos(doc, levelId, ref roomDataList, ref collection, true))
			{
				return false;
			}
			List<WallData> list = new List<WallData>();
			if (!OutWallFilter.GetWallDataInfos(wallsByLevel, ref list))
			{
				return false;
			}
			if (includLinkDocument)
			{
				list.AddRange(collection);
			}
			return OutWallFilter.SearchByRooms(roomDataList, list, ref outWallList, ref isOrientationRight);
		}

		private static bool SearchByPlanView(Autodesk.Revit.DB.Document doc, ElementId viewId, ref List<WallData> outWallList, ref List<bool> isOrientationRight, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			List<Wall> allWallByLevel = OutWallFilter.GetAllWallByLevel(doc, viewId);
			Level genLevel = (doc.GetElement(viewId) as ViewPlan).GenLevel;
			List<RoomData> roomDataList = new List<RoomData>();
			List<WallData> collection = new List<WallData>();
			if (!OutWallFilter.GetRoomDataInfos(doc, genLevel.Id, ref roomDataList, ref collection, IsOpenTran))
			{
				return false;
			}
			List<WallData> list = new List<WallData>();
			if (!OutWallFilter.GetWallDataInfos(allWallByLevel, ref list))
			{
				return false;
			}
			if (includLinkDocument)
			{
				list.AddRange(collection);
			}
			return OutWallFilter.SearchByRooms(roomDataList, list, ref outWallList, ref isOrientationRight);
		}

		private static bool SearchByPlanView(Autodesk.Revit.DB.Document doc, ElementId viewId, List<Wall> wallsByLevel, ref List<WallData> outWallList, ref List<bool> isOrientationRight, bool includLinkDocument = false, bool IsOpenTran = true)
		{
			ViewPlan viewPlan = doc.GetElement(viewId) as ViewPlan;
			if (viewPlan == null)
			{
				return false;
			}
			Level genLevel = viewPlan.GenLevel;
			List<RoomData> roomDataList = new List<RoomData>();
			List<WallData> collection = new List<WallData>();
			if (!OutWallFilter.GetRoomDataInfos(doc, genLevel.Id, ref roomDataList, ref collection, IsOpenTran))
			{
				return false;
			}
			List<WallData> list = new List<WallData>();
			if (!OutWallFilter.GetWallDataInfos(wallsByLevel, ref list))
			{
				return false;
			}
			if (includLinkDocument)
			{
				list.AddRange(collection);
			}
			return OutWallFilter.SearchByRooms(roomDataList, list, ref outWallList, ref isOrientationRight);
		}

		public static bool SearchOutWall(Autodesk.Revit.DB.Document doc, ElementId viewId, List<Wall> lstWallsByLevel, ref List<WallData> outWallList, bool IsOpenTran = true)
		{
			bool includLinkDocument = false;
			List<bool> list = new List<bool>();
			return OutWallFilter.SearchByPlanView(doc, viewId, lstWallsByLevel, ref outWallList, ref list, includLinkDocument, IsOpenTran);
		}

		private static bool SearchByRooms(List<RoomData> roomDataList, List<WallData> wallsByLevel, ref List<WallData> outWallDataList, ref List<bool> isOrientationRight)
		{
			foreach (RoomData roomData in roomDataList)
			{
				foreach (RoomData.BoundingSegment segment in roomData.WallSegmentList)
				{
					for (int i = 0; i < wallsByLevel.Count; i++)
					{
						WallData findedInfos = wallsByLevel[i];
						OutWallFilter.SearchBySegment(segment, ref findedInfos);
						wallsByLevel[i].SetFindedInfos(findedInfos);
					}
				}
			}
			foreach (WallData wallData in wallsByLevel)
			{
				WallData.OutWallOrientations outWallOrientations = WallData.OutWallOrientations.OWO_Two_Sided;
				if (wallData.IsOutWall(ref outWallOrientations))
				{
					outWallDataList.Add(wallData);
					bool item = false;
					wallData.IsOrientationRight(ref item);
					isOrientationRight.Add(item);
				}
			}
			return true;
		}

		private static bool GetRoomDataInfos(Autodesk.Revit.DB.Document doc, ElementId levelId, ref List<RoomData> roomDataList, ref List<WallData> linkedWallWithRoomBoundary, bool IsOpenTran = true)
		{
			if (!IsOpenTran)
			{
				try
				{
					Level level = doc.GetElement(levelId) as Level;
					ElementId floorPlanViewIdByLevel = OutWallFilter.GetFloorPlanViewIdByLevel(doc, level);
					if (floorPlanViewIdByLevel == null)
					{
						return false;
					}
					OutWallFilter.RemoveRooms(doc, floorPlanViewIdByLevel);
					roomDataList = OutWallFilter.Create2ndGetRoomDataInfos(doc, level, ref linkedWallWithRoomBoundary);
				}
				catch (Exception)
				{
					return false;
				}
				return true;
			}
			Transaction transaction = null;
			bool result;
			try
			{
				transaction = new Transaction(doc);
				transaction.Start("get room data");
				Level level2 = doc.GetElement(levelId) as Level;
				ElementId floorPlanViewIdByLevel2 = OutWallFilter.GetFloorPlanViewIdByLevel(doc, level2);
				if (floorPlanViewIdByLevel2 == null)
				{
					if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
					{
						transaction.RollBack();
					}
					result = false;
				}
				else
				{
					OutWallFilter.RemoveRooms(doc, floorPlanViewIdByLevel2);
					roomDataList = OutWallFilter.Create2ndGetRoomDataInfos(doc, level2, ref linkedWallWithRoomBoundary);
					if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
					{
						transaction.RollBack();
					}
					result = true;
				}
			}
			catch (Exception)
			{
				if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
				{
					transaction.RollBack();
				}
				result = false;
			}
			return result;
		}

		private static List<Wall> GetAllWallByLevel(Autodesk.Revit.DB.Document doc, ElementId viewId)
		{
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc, viewId);
			double elevation = (doc.GetElement(viewId) as ViewPlan).GenLevel.Elevation;
			IEnumerable<Element> enumerable = filteredElementCollector.OfClass(typeof(Wall)).ToElements();
			List<Wall> list = new List<Wall>();
			foreach (Element element in enumerable)
			{
				Wall wall = element as Wall;
				if (wall.ParametersMap.Contains("已附着顶部") || wall.ParametersMap.Contains("Top is Attached"))
				{
					double num = 0.0;
					double val = 0.0;
					OutWallFilter.GetWallElevations(doc, wall, ref num, ref val);
					if (Geometry.GreaterThan(val, elevation))
					{
						list.Add(wall);
					}
				}
			}
			return list;
		}

		private static void GetWallElevations(Autodesk.Revit.DB.Document doc, Wall wall, ref double bottom, ref double top)
		{
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			Level level = doc.GetElement(elementId) as Level;
			bottom = level.Elevation;
			double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
			bottom += num;
			ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId2 == ElementId.InvalidElementId)
			{
				double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
				top = bottom + num2;
				return;
			}
			Level level2 = doc.GetElement(elementId2) as Level;
			top = level2.Elevation;
			double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
			top += num3;
		}

		private static ElementId GetFloorPlanViewIdByLevel(Autodesk.Revit.DB.Document doc, Level level)
		{
			foreach (Element element in new FilteredElementCollector(doc).OfClass(typeof(Autodesk.Revit.DB.View)).ToElements())
			{
				Autodesk.Revit.DB.View view = (Autodesk.Revit.DB.View)element;
				try
				{
					if (view.GenLevel != null)
					{
						if (((int)view.ViewType == 1 || (int)view.ViewType == 115) && Geometry.IsEqual(view.GenLevel.Elevation, level.Elevation))
						{
							return view.Id;
						}
					}
				}
				catch (Exception)
				{
				}
			}
			return null;
		}

		private static bool GetWallDataInfos(List<Wall> wallsByLevel, ref List<WallData> wallDataList)
		{
			foreach (Wall wall in wallsByLevel)
			{
				if (wall != null)
				{
					WallData wallData = OutWallFilter.GetWallData(wall);
					wallDataList.Add(wallData);
				}
			}
			return true;
		}

		private static WallData GetWallData(Wall wall)
		{
			double num = wall.Width / 2.0;
			Curve curve = (wall.Location as LocationCurve).Curve;
			if (curve.GetType() == typeof(Line))
			{
				Line line = curve as Line;
				EdgeInfo positiveLine = new EdgeInfo(Geometry.OffsetLine(line, num));
				EdgeInfo negativeLine = new EdgeInfo(Geometry.OffsetLine(line, -num));
				EdgeInfo centerLine = new EdgeInfo(line);
				return new WallData(positiveLine, negativeLine, centerLine, wall.Document, wall.Id);
			}
			if (curve.GetType() == typeof(Arc))
			{
				Arc arc = curve as Arc;
				EdgeInfo positiveLine2 = new EdgeInfo(Geometry.OffsetArc(arc, num, null));
				EdgeInfo negativeLine2 = new EdgeInfo(Geometry.OffsetArc(arc, -num, null));
				EdgeInfo centerLine2 = new EdgeInfo(arc);
				return new WallData(positiveLine2, negativeLine2, centerLine2, wall.Document, wall.Id);
			}
			return null;
		}

		private static void RemoveRooms(Autodesk.Revit.DB.Document doc, ElementId viewId)
		{
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc, viewId).OfClass(typeof(SpatialElement));
			List<ElementId> list = new List<ElementId>();
			foreach (Element element in filteredElementCollector)
			{
				Room room = ((SpatialElement)element) as Room;
				if (room != null)
				{
					list.Add(room.Id);
				}
			}
			doc.Delete(list);
		}

		private static List<RoomData> Create2ndGetRoomDataInfos(Autodesk.Revit.DB.Document doc, Level level, ref List<WallData> linkedWallWithRoomBoundary)
		{
			List<RoomData> list = new List<RoomData>();
			double num = AssistFunc.mmToFeet(10.0);
			num *= num;
			SpatialElementBoundaryOptions spatialElementBoundaryOptions = new SpatialElementBoundaryOptions();
			foreach (object obj in doc.get_PlanTopology(level).Circuits)
			{
				PlanCircuit planCircuit = (PlanCircuit)obj;
				if (!planCircuit.IsRoomLocated && !Geometry.LessThan(planCircuit.Area, num))
				{
					SpatialElement spatialElement = doc.Create.NewRoom(null, planCircuit);
					List<RoomData.BoundingSegment> list2 = new List<RoomData.BoundingSegment>();
					foreach (IList<BoundarySegment> list3 in spatialElement.GetBoundarySegments(spatialElementBoundaryOptions))
					{
						foreach (BoundarySegment segment in list3)
						{
							Element element = segment.GetElement(doc);
							if (element != null)
							{
								if (element.GetType() == typeof(Wall))
								{
									RoomData.BoundingSegment item = new RoomData.BoundingSegment(new EdgeInfo(segment.GetCurve()), doc, element.Id);
									list2.Add(item);
								}
								else if (element.GetType() == typeof(RevitLinkInstance) && !(RevitVersion.GetVersionNumber(doc) == "2015"))
								{
									Document linkDocument = (element as RevitLinkInstance).GetLinkDocument();
									ElementId boundarySegmentLinkElementId = UnifiedModified.GetBoundarySegmentLinkElementId(segment);
									if (!(boundarySegmentLinkElementId == ElementId.InvalidElementId))
									{
										Wall wall = null;
										if (OutWallFilter.IsWall(linkDocument, boundarySegmentLinkElementId, ref wall))
										{
											RoomData.BoundingSegment item2 = new RoomData.BoundingSegment(new EdgeInfo(segment.GetCurve()), linkDocument, boundarySegmentLinkElementId);
											list2.Add(item2);
											WallData wallData = OutWallFilter.GetWallData(wall);
											linkedWallWithRoomBoundary.Add(wallData);
										}
									}
								}
							}
						}
					}
					RoomData item3 = new RoomData(list2);
					list.Add(item3);
				}
			}
			return list;
		}

		private static bool IsWall(Autodesk.Revit.DB.Document doc, ElementId id, ref Wall wall)
		{
			Element element = doc.GetElement(id);
			if (element == null)
			{
				return false;
			}
			if (element.GetType() != typeof(Wall))
			{
				return false;
			}
			wall = (element as Wall);
			return true;
		}

		private static void SearchBySegment(RoomData.BoundingSegment segment, ref WallData wallData)
		{
			if (segment.WallId.IntegerValue != wallData.Id.IntegerValue)
			{
				return;
			}
			double tolerance = 0.01;
			if (!segment.Curve.IsArc && !wallData.PositiveLine.IsArc)
			{
				OutWallFilter.SearchLineSegment(segment, tolerance, ref wallData);
				return;
			}
			if (segment.Curve.IsArc && wallData.PositiveLine.IsArc)
			{
				OutWallFilter.SearchArcSegment(segment, tolerance, ref wallData);
			}
		}

		private static void SearchLineSegment(RoomData.BoundingSegment segment, double tolerance, ref WallData wallData)
		{
			Line curve = wallData.CenterLine.ConvertToCurve() as Line;
			EdgeInfo curve2 = segment.Curve;
			EdgeInfo positiveLine = wallData.PositiveLine;
			XYZ pt = null;
			XYZ pt2 = null;
			Geometry.OverlapType overlapType = OverlapFinder.OverlapFind(curve2, positiveLine, ref pt, ref pt2);
			if (overlapType != Geometry.OverlapType._OT_None && overlapType != Geometry.OverlapType._OT_Linked)
			{
				XYZ project = OutWallFilter.GetProject(curve, pt);
				XYZ project2 = OutWallFilter.GetProject(curve, pt2);
				wallData.AddPositiveFindedInfo(new EdgeInfo(project, project2, 0.0));
			}
			EdgeInfo negativeLine = wallData.NegativeLine;
			overlapType = OverlapFinder.OverlapFind(curve2, negativeLine, ref pt, ref pt2);
			if (overlapType != Geometry.OverlapType._OT_None && overlapType != Geometry.OverlapType._OT_Linked)
			{
				XYZ project3 = OutWallFilter.GetProject(curve, pt);
				XYZ project4 = OutWallFilter.GetProject(curve, pt2);
				wallData.AddNegativeFindedInfo(new EdgeInfo(project3, project4, 0.0));
			}
		}

		private static XYZ GetProject(Curve curve, XYZ pt)
		{
			return curve.Project(pt).XYZPoint;
		}

		private static void SearchArcSegment(RoomData.BoundingSegment segment, double tolerance, ref WallData wallData)
		{
			Arc arc = wallData.CenterLine.ConvertToCurve() as Arc;
			XYZ center = arc.Center;
			EdgeInfo curve = segment.Curve;
			EdgeInfo positiveLine = wallData.PositiveLine;
			XYZ pt = null;
			XYZ pt2 = null;
			Geometry.OverlapType overlapType = OverlapFinder.OverlapFind(curve, positiveLine, ref pt, ref pt2);
			if (overlapType != Geometry.OverlapType._OT_None && overlapType != Geometry.OverlapType._OT_Linked)
			{
				XYZ project = OutWallFilter.GetProject(arc, pt);
				XYZ project2 = OutWallFilter.GetProject(arc, pt2);
				double bulge = Geometry.GetBulge(project, project2, center, true);
				wallData.AddPositiveFindedInfo(new EdgeInfo(project, project2, bulge));
			}
			EdgeInfo negativeLine = wallData.NegativeLine;
			overlapType = OverlapFinder.OverlapFind(curve, negativeLine, ref pt, ref pt2);
			if (overlapType != Geometry.OverlapType._OT_None && overlapType != Geometry.OverlapType._OT_Linked)
			{
				XYZ project3 = OutWallFilter.GetProject(arc, pt);
				XYZ project4 = OutWallFilter.GetProject(arc, pt2);
				double bulge2 = Geometry.GetBulge(project3, project4, center, true);
				wallData.AddNegativeFindedInfo(new EdgeInfo(project3, project4, bulge2));
			}
		}
	}
}
